Skip to content

feat(functions): implement VSTACK and HSTACK (HF-71)#1698

Open
marcin-kordas-hoc wants to merge 1 commit into
developfrom
feature/hf-71-vstack-hstack
Open

feat(functions): implement VSTACK and HSTACK (HF-71)#1698
marcin-kordas-hoc wants to merge 1 commit into
developfrom
feature/hf-71-vstack-hstack

Conversation

@marcin-kordas-hoc

@marcin-kordas-hoc marcin-kordas-hoc commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

What

Implements two new array-spilling functions, VSTACK and HSTACK, in ArrayPlugin.

  • VSTACK(array1, [array2], ...) — stacks arrays vertically. Result height = sum of input heights, width = max of input widths. Narrower inputs are padded on the right with #N/A.
  • HSTACK(array1, [array2], ...) — stacks arrays horizontally. Result width = sum of input widths, height = max of input heights. Shorter inputs are padded at the bottom with #N/A.

Behaviour matches Excel 365 / Google Sheets, with the documented HyperFormula nuances below.

Scope

  • src/interpreter/plugin/ArrayPlugin.tsVSTACK/HSTACK metadata, methods, and *ArraySize parse-time size calculations (shared stackSubChecks + padRowToWidth helpers).
  • src/i18n/languages/*.ts — function-name entries for all 18 language packs (enUS inherits from enGB). The names are identical across locales, matching Excel's convention for these functions.
  • docs/guide/built-in-functions.md — entries for both functions.
  • CHANGELOG.mdAdded entry under [Unreleased].

HyperFormula nuances vs. Excel

  • Empty cells pass through as empty (null) rather than coercing to 0. HyperFormula preserves the empty value; Excel (which has no empty-result cell) displays 0. The stacked structure is identical.
  • A bare scalar argument that is itself an error short-circuits the whole call to that error, the same way every runFunction-based function behaves (e.g. ABS, FILTER). Errors located inside an input range pass through per cell, preserving their type (matches Excel).

Test coverage

Unit tests live in the private tests repository: handsontable/hyperformula-tests#18 (54 cases: 27 VSTACK + 27 HSTACK), mirroring a validated Excel 365 oracle. They cover same-width/height stacks, dimension mismatch with #N/A padding, scalars, single-arg passthrough, mixed types, error passthrough, empty cells, nested VSTACK/HSTACK, and integration with SEQUENCE/TRANSPOSE.

🤖 Generated with Claude Code

Add the VSTACK and HSTACK array-manipulation functions to ArrayPlugin,
mirroring the existing FILTER implementation pattern.

- VSTACK stacks input arrays vertically: result height = sum of input
  heights, width = max input width. Narrower rows are padded on the
  right with #N/A.
- HSTACK stacks input arrays horizontally: result width = sum of input
  widths, height = max input height. Shorter columns are padded at the
  bottom with #N/A.
- Both are variadic (repeatLastArgs: 1) and accept ranges, array
  literals and scalars (FunctionArgumentType.RANGE,
  enableArrayArithmeticForArguments).
- Result dimensions are declared at parse time via the
  sizeOfResultArrayMethod (vstackArraySize / hstackArraySize) so the
  result spills correctly, consistent with HyperFormula's parse-time
  array sizing. Padding uses ErrorType.NA (HyperFormula has no #CALC!).
- Add translations for all 17 built-in language packs (English name in
  every locale, matching Excel/Sheets, which do not localize these
  functions). enUS inherits enGB.
- Document both functions in the built-in functions guide and add a
  changelog entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@netlify

netlify Bot commented Jun 23, 2026

Copy link
Copy Markdown

Deploy Preview for hyperformula-dev-docs ready!

Name Link
🔨 Latest commit 4abdae5
🔍 Latest deploy log https://app.netlify.com/projects/hyperformula-dev-docs/deploys/6a3a5348849cec00088192ba
😎 Deploy Preview https://deploy-preview-1698--hyperformula-dev-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@qunabu

qunabu commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Task linked: HF-71 Implement VSTACK and HSTACK functions

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4abdae5. Configure here.

const sourceRow = row < data.length ? data[row] : undefined
for (let col = 0; col < range.width(); col++) {
result[row].push(sourceRow !== undefined ? sourceRow[col] : new CellError(ErrorType.NA, ErrorMessage.ValueNotFound))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HSTACK omits NA row padding

Medium Severity

In hstack, when a row exists but has fewer columns than the range width (including an empty row array), cells beyond the row length are taken from sourceRow[col] and become undefined instead of #N/A. vstack pads short rows via padRowToWidth, so stacked results can disagree with Excel and with VSTACK on jagged inputs such as FILTER output.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4abdae5. Configure here.

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

Performance comparison of head (4abdae5) vs base (72205bd)

                                     testName |   base |   head | change
------------------------------------------------------------------------
                                      Sheet A | 479.75 |  492.3 | +2.62%
                                      Sheet B | 153.34 | 155.85 | +1.64%
                                      Sheet T | 135.57 | 135.56 | -0.01%
                                Column ranges | 510.56 | 517.37 | +1.33%
Sheet A:  change value, add/remove row/column |  14.84 |  15.56 | +4.85%
 Sheet B: change value, add/remove row/column | 134.65 | 131.19 | -2.57%
                   Column ranges - add column | 154.02 | 155.12 | +0.71%
                Column ranges - without batch | 464.11 |  477.4 | +2.86%
                        Column ranges - batch | 120.17 | 122.75 | +2.15%

@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.16%. Comparing base (72205bd) to head (4abdae5).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff            @@
##           develop    #1698   +/-   ##
========================================
  Coverage    97.16%   97.16%           
========================================
  Files          176      176           
  Lines        15322    15365   +43     
  Branches      3387     3393    +6     
========================================
+ Hits         14887    14930   +43     
  Misses         435      435           
Files with missing lines Coverage Δ
src/i18n/languages/csCZ.ts 100.00% <ø> (ø)
src/i18n/languages/daDK.ts 100.00% <ø> (ø)
src/i18n/languages/deDE.ts 100.00% <ø> (ø)
src/i18n/languages/enGB.ts 100.00% <ø> (ø)
src/i18n/languages/esES.ts 100.00% <ø> (ø)
src/i18n/languages/fiFI.ts 100.00% <ø> (ø)
src/i18n/languages/frFR.ts 100.00% <ø> (ø)
src/i18n/languages/huHU.ts 100.00% <ø> (ø)
src/i18n/languages/idID.ts 100.00% <ø> (ø)
src/i18n/languages/itIT.ts 100.00% <ø> (ø)
... and 8 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants